/*
*
* 本算法10W次随机14张牌胡牌检测耗时4700毫秒，其中随机生成10万次14张牌，耗时2000毫秒。
* 所以10W次随机14张牌胡牌计算为2700毫秒  cpu5代I7
* 本算法支持七对判断，支持癞子，支持随机癞子，支持风牌
* 牌的数字1-9筒  11-19条  21-29万 31-37 东，南，西，北，中，发，白
* 对外暴露的方法
* checkHu(arr)检测是否胡牌
* canHu(arr)提示能胡什么牌
* 其他如吃，碰，杠检测比较简单，自行完成。
*
* */
var isQiDui = true; //是否默认支持七对
var isLaiZi = true; //是否有癞子
var laiZi = 37; //默认白板是癞子
var isFeng = true; //是否开启风牌

function sortByAsc(arr) {
	arr.sort(function (x, y) {
		return x - y;
	});
	return arr;
}

function isRepeat(arr) {
	var hash = {};
	for (var i in arr) {
		if (hash[arr[i]])
			return true;
		hash[arr[i]] = true;
	}
	return false;
}

function checkIsThirteenMess(arr) {
	if (arr.length < 14) {
		return false;
	} else {
		sortByAsc(arr);
		if (isRepeat(arr)) {
			return false;
		}

		var isThirteen = true;
		var tong = [];
		var tiao = [];
		var wan = [];
		var feng = [];

		for (var i = 0; i < 14; i++) {
			if (arr[i] > 0 && arr[i] < 10) {
				tong.push(arr[i]);
			} else if (arr[i] > 10 && arr[i] < 20) {
				tiao.push(arr[i]);
			} else if (arr[i] > 20 && arr[i] < 30) {
				wan.push(arr[i]);
			} else {
				feng.push(arr[i]);
			}
		}

		var wordNum = tong.length + tiao.length + wan.length;

		if (wordNum >= 7 && wordNum <= 9) {
			for (var i = 0; i < tong.length; i++) {
				if (tong[i + 1] && (tong[i + 1] - tong[i] < 3)) {
					isThirteen = false;
					break;
				}
			}

			for (var i = 0; i < tiao.length; i++) {
				if (tiao[i + 1] && (tiao[i + 1] - tiao[i] < 3)) {
					isThirteen = false;
					break;
				}
			}

			for (var i = 0; i < wan.length; i++) {
				if (wan[i + 1] && (wan[i + 1] - wan[i] < 3)) {
					isThirteen = false;
					break;
				}
			}
		} else {
			isThirteen = false;
		}

		return isThirteen;
	}
}

function checkThirteenMess(arr) {
	if (arr.length < 14) {
		return {
			hu: false,
			pattern: "normal"
		};
	} else {
		sortByAsc(arr);
		var arrCopy = arr.concat();

		// 去掉万能牌
		var index = arr.indexOf(37);
		var laiZiNum = (index > -1) ? (14 - index) : 0;

		if (laiZiNum > 0) {
			arr.splice(index, laiZiNum);
		}

		if (isRepeat(arr)) {
			return {
				hu: false,
				pattern: "normal"
			};
		}

		var majhongs = [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19, 21, 22, 23, 24, 25, 26, 27, 28, 29, 31, 32, 33, 34, 35, 36, 37];

		if (laiZiNum == 0) {
			if (checkIsThirteenMess(arrCopy)) {
				return {
					hu: true,
					pattern: "thirteenMess"
				};
			}

			return {
				hu: false,
				pattern: "normal"
			};
		} else if (laiZiNum == 1) {
			for (var j = 0; j < majhongs.length; j++) {
				if (checkIsThirteenMess([].concat(arr, majhongs[j]))) {
					return {
						hu: true,
						pattern: "thirteenMess"
					};
				}
			}

			return {
				hu: false,
				pattern: "normal"
			};
		} else if (laiZiNum == 2) {
			for (var j = 0; j < majhongs.length; j++) {
				for (var k = j; k < majhongs.length; k++) {
					if (checkIsThirteenMess([].concat(arr, [majhongs[j], majhongs[k]]))) {
						return {
							hu: true,
							pattern: "thirteenMess"
						};
					}
				}
			}

			return {
				hu: false,
				pattern: "normal"
			};
		} else if (laiZiNum == 3) {
			for (var j = 0; j < majhongs.length; j++) {
				for (var k = j; k < majhongs.length; k++) {
					for (var m = k; m < majhongs.length; m++) {
						if (checkIsThirteenMess([].concat(arr, [majhongs[j], majhongs[k], majhongs[m]]))) {
							return {
								hu: true,
								pattern: "thirteenMess"
							};
						}
					}
				}
			}

			return {
				hu: false,
				pattern: "normal"
			};
		} else {
			return {
				hu: true,
				pattern: "thirteenMess"
			};
		}
	}
}

//返回牌在二位数组中位置坐标
var getPos = function (pai) {
	var x = parseInt(pai / 10);//getType(pai);
	var y = pai % 10 - 1;
	return {x: x, y: y};
};

//将一个牌数组转为二维数组
var transform = function (pais) {
	var result = [
		[0, 0, 0, 0, 0, 0, 0, 0, 0], //筒
		[0, 0, 0, 0, 0, 0, 0, 0, 0], //条
		[0, 0, 0, 0, 0, 0, 0, 0, 0], //万
		[0, 0, 0, 0, 0, 0, 0] //风
	];
	for (var i = pais.length - 1; i >= 0; i--) {
		var pos = getPos(pais[i]);
		result[pos.x][pos.y] += 1;
	}
	return result;
};

var copyTransformedPais = function (transformedPais) {
	var result = [];
	for (var i = 0; i < transformedPais.length; i++) {
		result.push([].concat(transformedPais[i]));
	}
	return result;
};

//获取癞子的个数,并且在之后移除转换牌中的所有癞子
var getLaiZiCount = function (transformedPais) {
	var pos = getPos(laiZi);
	var count = transformedPais[pos.x][pos.y];
	transformedPais[pos.x][pos.y] = 0;
	return count;
};

//获取所有可能的将的位置
var getAllJiang = function (transformedPais) {
	var result = [];
	for (var i = 0; i < transformedPais.length; i++) {
		for (var j = 0; j < transformedPais[i].length; j++) {
			if (transformedPais[i][j] > 0) {
				result.push(transformedPais[i][j] === 1 ? [[i, j]] : [[i, j], [i, j]]);
			}
		}
	}
	result.sort((a, b) => {
		return a.length - b.length;
	});
	return result;
};

var checkQiDui = function (transformedPais, laiZiCount) {
	var duiZi = 0;
	for (var i = 0; i < transformedPais.length; i++) {
		for (var j = 0; j < transformedPais[i].length; j++) {
			if (transformedPais[i][j] < 2)
				continue;
			switch (transformedPais[i][j]) {
				case 2:
					duiZi += 1;
					break;
				case 3:
					duiZi += 1;
					break;
				case 4:
					duiZi += 2;
					break;
			}
		}
	}
	return laiZiCount > 6 - duiZi;
};

var clearOnePais = function (transformedPaiArray, maxLaiZi, i, needLaiZi) {
	if (transformedPaiArray[i] === 0 || needLaiZi.n > maxLaiZi) {
		return;
	}
	if (transformedPaiArray[i] >= 3) {//如果是三个一样的，直接移除
		transformedPaiArray[i] -= 3;
		return clearOnePais(transformedPaiArray, maxLaiZi, i, needLaiZi);
	}
	if (transformedPaiArray[i] > 0 && transformedPaiArray[i + 1] != undefined && transformedPaiArray[i + 1] > 0 && transformedPaiArray[i + 2] != undefined && transformedPaiArray[i + 2] > 0) {
		transformedPaiArray[i] -= 1;
		transformedPaiArray[i + 1] -= 1;
		transformedPaiArray[i + 2] -= 1;
		return clearOnePais(transformedPaiArray, maxLaiZi, i, needLaiZi);
	}
	if (transformedPaiArray[i] === 2) {//如果是三个一样的，直接移除
		transformedPaiArray[i] -= 2;
		needLaiZi.n += 1;
		return clearOnePais(transformedPaiArray, maxLaiZi, i, needLaiZi);
	}
	if (transformedPaiArray[i] > 0 && transformedPaiArray[i + 1] != undefined && transformedPaiArray[i + 1] > 0) {
		transformedPaiArray[i] -= 1;
		transformedPaiArray[i + 1] -= 1;
		needLaiZi.n += 1;
		return clearOnePais(transformedPaiArray, maxLaiZi, i, needLaiZi);
	}

	if (transformedPaiArray[i] > 0 && transformedPaiArray[i + 2] != undefined && transformedPaiArray[i + 2] > 0) {
		transformedPaiArray[i] -= 1;
		transformedPaiArray[i + 2] -= 1;
		needLaiZi.n += 1;
		return clearOnePais(transformedPaiArray, maxLaiZi, i, needLaiZi);
	}
	transformedPaiArray[i] -= 1;
	needLaiZi.n += 2;
};

function checkHu(pais) {
	var paisCopy = pais.concat();
	//检测是不是十三不搭
	if (paisCopy.length === 14 && checkThirteenMess(paisCopy).hu) {
		return {
			hu: true,
			pattern: "thirteenMess"
		};
	}

	var transformedPais = transform(pais);
	var laiZiCount = isLaiZi ? getLaiZiCount(transformedPais) : 0;
	if (laiZiCount === 4) {
		return {
			hu: true,
			pattern: "4laiZi"
		};
	}
	if (laiZiCount === 1 && pais.length == 2) {
		return {
			hu: true,
			pattern: "normal"
		};
	}

	//检测七对
	if (isQiDui && pais.length === 14 && checkQiDui(transformedPais, laiZiCount)) {
		return {
			hu: true,
			pattern: "qidui"
		};
	}
	var allJiang = getAllJiang(transformedPais);
	//随机拿走一对做将，判断剩下的牌能否成为整扑
	for (var i = 0; i < allJiang.length; i++) {
		var laiZi = laiZiCount;
		var copyedTransformedPais = copyTransformedPais(transformedPais); //复制一份牌
		if (allJiang[i].length == 1) {
			laiZi -= 1;
		}
		//移除将占用的牌
		for (var j = 0; j < allJiang[i].length; j++) {
			copyedTransformedPais[allJiang[i][j][0]][[allJiang[i][j][1]]] -= 1;
		}

		//计算筒子，条子，万子成为整朴需要的癞子个数
		for (var j = 0; j < 3; j++) {
			var reversePais = [].concat(copyedTransformedPais[j]);
			var oneNeedLaiZiCount = getNeedLaiZiCount(copyedTransformedPais[j], laiZi);
			if (oneNeedLaiZiCount > 0) {
				var oneNeedLaiZiCountReverse = getNeedLaiZiCount(reversePais.reverse(), laiZi);
				if (oneNeedLaiZiCount > oneNeedLaiZiCountReverse) {
					oneNeedLaiZiCount = oneNeedLaiZiCountReverse;
				}
			}
			laiZi -= oneNeedLaiZiCount;
			if (laiZi < 0) {
				break;
			}
		}

		if (isFeng && laiZi >= 0) { //如果需要风牌，则加入风牌需要的癞子数量
			laiZi -= getFengNeedLaiZiCount(copyedTransformedPais[3]);
		}

		if (laiZi >= 0) {
			return {
				hu: true,
				pattern: "normal"
			};
		}
	}
	return {
		hu: false,
		pattern: "normal"
	};
}

//获取风牌成为整扑所需要的癞子数量
function getFengNeedLaiZiCount(transformedPaiArray) {
	var needLaiZiCount = 0;
	for (var i = 0; i < transformedPaiArray.length; i++) {
		if (transformedPaiArray[i] === 0 || transformedPaiArray[i] === 3) {
			continue;
		}
		if (transformedPaiArray[i] == 2) {
			needLaiZiCount += 1;
		} else {
			needLaiZiCount += 2;
		}
	}
	return needLaiZiCount;
}

//获取单门（筒，条，万）成为整扑需要的牌有哪些,所需癞子的个数
function getNeedLaiZiCount(transformedPaiArray, maxLaiZi) {
	var needLaiZi = {n: 0};
	for (var i = 0; i < transformedPaiArray.length; i++) {
		if (transformedPaiArray[i] === 0) {
			continue;
		}
		clearOnePais(transformedPaiArray, maxLaiZi, i, needLaiZi);
	}
	return needLaiZi.n;
}

//判断当前牌型听什么牌
function canHu(arr) {
	var result = {
		hu: [],
		baotou: false,
		pattern: []
	};
	var majhongs = [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19, 21, 22, 23, 24, 25, 26, 27, 28, 29];
	if (isFeng) {
		majhongs = majhongs.concat([31, 32, 33, 34, 35, 36, 37]);
	} else if (isLaiZi && laiZi > 30) {
		majhongs.push(laiZi);
	}

	if (checkBaoTou(arr)) {
		result.baotou = true;
	}

	for (var i = 0; i < majhongs.length; i++) {
		var thirteenResult = checkThirteenMess([].concat(arr, majhongs[i]));
		if (thirteenResult.hu) {
			result.hu.push(majhongs[i]);
			result.pattern.push(thirteenResult.pattern);
		} else {
			var normalResult = checkHu([].concat(arr, majhongs[i]));
			if (normalResult.hu) {
				result.hu.push(majhongs[i]);
				result.pattern.push(normalResult.pattern);
			}
		}
	}

	return result;
}

// 检查是不是手牌能不能爆头
function checkBaoTou(arr) {
	var holds = JSON.parse(JSON.stringify(arr));
	sortByAsc(holds);

	var transformedQiPais = transform(holds);
	var laiZiQiCount = isLaiZi ? getLaiZiCount(transformedQiPais) : 0;

	//先检测七对
	if (arr.length === 13 && checkQiDui(transformedQiPais, laiZiQiCount)) {
		return true;
	}

	//检查有没有万牌，如果有，去掉一张万能牌，看下剩余的牌能不能成为整扑
	if (holds[holds.length - 1] != 37) {
		return false;
	}

	holds.splice(holds.length - 1, 1);

	var transformedPais = transform(holds);
	var laiZiCount = isLaiZi ? getLaiZiCount(transformedPais) : 0;

	var copyedTransformedPais = copyTransformedPais(transformedPais); //复制一份牌

	//计算筒子，条子，万子成为整朴需要的癞子个数
	for (var j = 0; j < 3; j++) {
		var reversePais = [].concat(copyedTransformedPais[j]);
		var oneNeedLaiZiCount = getNeedLaiZiCount(copyedTransformedPais[j], laiZiCount);
		if (oneNeedLaiZiCount > 0) {
			var oneNeedLaiZiCountReverse = getNeedLaiZiCount(reversePais.reverse(), laiZiCount);
			if (oneNeedLaiZiCount > oneNeedLaiZiCountReverse) {
				oneNeedLaiZiCount = oneNeedLaiZiCountReverse;
			}
		}
		laiZiCount -= oneNeedLaiZiCount;
		if (laiZiCount < 0) {
			break;
		}
	}

	if (isFeng && laiZi >= 0) { //如果需要风牌，则加入风牌需要的癞子数量
		laiZiCount -= getFengNeedLaiZiCount(copyedTransformedPais[3]);
	}

	if (laiZiCount >= 0) {
		return true;
	}

	return false;
}

function pengNumInHolds(arr, max) {
	// 去掉万能牌
	var _arr = arr.concat();
	var index = _arr.indexOf(37);
	var laiZiNum = (index > -1) ? (_arr.length - index) : 0;

	if (laiZiNum > 0) {
		_arr.splice(index, laiZiNum);
	}

	var _res = [];
	_arr.sort();
	for (var i = 0; i < _arr.length;) {
		var count = 0;
		for (var j = i; j < _arr.length; j++) {
			if (_arr[i] == _arr[j]) {
				count++;
			}
		}
		_res.push([_arr[i], count]);
		i += count;
	}

	var num = 0;
	for (var i = 0; i < _res.length; i++) {
		if (_res[i][1] >= max) {
			num++;
		}
	}

	return num;
}

exports.checkHu = checkHu;
exports.canHu = canHu;
exports.checkBaoTou = checkBaoTou;
exports.pengNumInHolds = pengNumInHolds;